package edu.hawaii.halealohacli.jcev.command;

import java.util.List;
import java.util.Map;
import javax.xml.datatype.XMLGregorianCalendar;
import org.wattdepot.client.BadXmlException;
import org.wattdepot.client.MiscClientException;
import org.wattdepot.client.NotAuthorizedException;
import org.wattdepot.client.ResourceNotFoundException;
import org.wattdepot.client.WattDepotClient;
import org.wattdepot.resource.sensordata.jaxb.SensorData;
import org.wattdepot.resource.source.jaxb.Source;
import org.wattdepot.util.tstamp.Tstamp;

/**
 * Sets the baseline values for a certain location on a certain date(optional).
 * If no date parameter is given, the day before current day is used by default.
 * 
 * @author Kamana
 *
 */
public class SetBaseline implements Command {

  /** name of the source. **/
  private String sourceName;
  /** to hold sensor data. **/
  private SensorData data;
  /** holds the baselineData values. **/
  private Map<String,Double> baselineData;
  /** holds the date in XMLGC format. **/
  private XMLGregorianCalendar thedate;
  /** boolean for test purposes. **/
  public boolean testArgs = false;
  
  Exception dateFormat = new Exception("Error: Incorrect date format."
      + "Correct format: yyyy-mm-dd");
  Exception incorrectArguments = new Exception("Error: Command and/or optional date entered wrong\n"
     + "Correct syntax: set-baseline [tower | lounge] [date]");
  
/**
 * Creates an object with baseline data.
 * 
 * @param client The client to be used
 * @param name Location for baseline
 * @param date Date in [yyyy-mm-dd] format
 * @param bmap the baseline data
 * @return initialized SetBaseline
 * @throws Exception inputs are invalid.
 */
  public SetBaseline setAll(WattDepotClient client, String name, 
              XMLGregorianCalendar date, Map<String,Double> bmap) 
      throws Exception {
    this.data = client.getLatestSensorData(name);
    this.sourceName = name;
    this.thedate = date;
    this.baselineData = bmap;
    baselineData = setBaselines(client,sourceName,thedate,baselineData);
    return this;
  }
  
  /**
   * Constructor, initialized by setAll().
   */
  public SetBaseline() {
    //Object is initialized by the setAll() thingy.
  }
  
  /**
   * Modified constructor that takes in additional Map<String,Double> parameter.
   * 
   * @param args User input
   * @param client Client to be used
   * @param bmap baselineData
   * @throws Exception Something wrong with parameters
   */
  public void execute2(String args, WattDepotClient client, Map<String,Double> bmap) 
  throws Exception {
    System.out.println("Set-Baseline code executing.");
    XMLGregorianCalendar tempXML = null;
    String tempLocation = null;
    if (args.contains(" ")) { //if there is a space
      System.out.println("We have a space!");      
      String[] splitArgs = args.split("[ ]");
   
      try {
        String test1 = splitArgs[0];
        String test2 = splitArgs[1];
        System.out.println("splitArgs[0]:" + test1);
        System.out.println("splitArgs[1]:" + test2);
      }
      catch (Exception e) {
        throw incorrectArguments;
      }
      
      tempLocation = setLocation(client,splitArgs[0]); //verify that sourceName is a valid entry
      tempXML = setDate(splitArgs[1]); //verify that date is valid
      System.out.println("are we there yet?");
    }
    else { //if there are no spaces
      System.out.println("There is no spaces!");
      tempLocation = setLocation(client,args);
      tempXML = setDate(null);
    }
    System.out.println("do somethingsomething!" + tempXML.toString());
    SetBaseline sb = new SetBaseline();
    sb = sb.setAll(client,tempLocation,tempXML,bmap);
    this.baselineData = sb.getBaselineData();
    System.out.println(sb.toString());
  }

/**
 * Retrieves data from the server and gets baseline data.
 * 
 * @param client The client to be used
 * @param name location for baseline data
 * @param calendar the date for baseline data
 * @param bmap the baseline data
 * @return Map<String,Double> containing baseline data
 * @throws Exception parameters are invalid
 */
  public Map<String,Double> setBaselines(WattDepotClient client, String name, 
                          XMLGregorianCalendar calendar, Map<String,Double> bmap) 
                              throws Exception {
    Map<String,Double> tempMap = bmap;
    XMLGregorianCalendar time1 = calendar;
    XMLGregorianCalendar time2 = Tstamp.incrementHours(time1, 1);
    try {
      for (int i = 0;i < 24;i++) {
        String hour = Integer.toString(time1.getHour());
        String mapKey = name.concat("-" + hour);
        double energy = 0;
      
          energy = client.getEnergyConsumed(name, time1, time2, 0);
      
        if (tempMap.containsKey(mapKey)) {
          tempMap.remove(mapKey);
          tempMap.put(mapKey, energy);
        }
        else {
          tempMap.put(mapKey, energy);
        }
        time1 = Tstamp.incrementHours(time1, 1);
        time2 = Tstamp.incrementHours(time2, 1);
      }
    }
    catch (NotAuthorizedException e) {
      System.err.println(e);
    }
    catch (ResourceNotFoundException e) {
      System.err.println(e);
    }
    catch (BadXmlException e) {
      System.err.println(e);
    }
    catch (MiscClientException e) {
      System.err.println(e);
    }
    return tempMap;
  }

  /**
   * Sets the location.
   * 
   * @param client the client to be used
   * @param locParameter the location to be verified
   * @return verified location
   * @throws Exception parameters are invalid
   */
  public String setLocation(WattDepotClient client, String locParameter) throws Exception {
    System.out.println("executing setLocation:");
    testArgs = false;
    String tempLoc = locParameter;
    List<Source> sources = client.getSources();
    boolean match = false;
    // Checks if the 2nd argument matches a source name. Throw exception if no matches.
    for (Source source : sources) {
      if (tempLoc.equals(source.getName())) {
        match = true;
        System.out.println("We have a match!");
        testArgs = true;
        break;
      }
    }
    if (!match) {
      testArgs = false;
      System.out.println("We don't have a match!");
      tempLoc = null;
      throw new Exception("Error: Source \"" + locParameter + "\" does not exist.");
    }
    return tempLoc;
    //this.testArg = true;
  }
  
/**
 * Takes a date string and returns in XMLGregorianCalender format.
 * 
 * @param dayParameter the date string
 * @return date in XMLGC format
 * @throws Exception parameters are invalid
 */
  public XMLGregorianCalendar setDate(String dayParameter) throws Exception {
    testArgs = false;
    XMLGregorianCalendar tempDate;
    System.out.println("Executing setCalendar:");
    int year;
    int month;
    int day;
    tempDate = data.getTimestamp();//initializes XMLGC thing
    tempDate.setTime(0, 0, 0);
    if (dayParameter == null) { //if there is no date string attached to the thingy...
      tempDate = Tstamp.incrementDays(tempDate, -1);//set the date to yesterday
      testArgs = true;
    }
    else {                      //if there is a date attached to the string...
      year = Integer.parseInt(dayParameter.substring(0, 4));
      month = Integer.parseInt(dayParameter.substring(5, 7));
      day = Integer.parseInt(dayParameter.substring(8, 10));
      try {
      tempDate.setYear(year);
      tempDate.setMonth(month);
      tempDate.setDay(day);
      testArgs = true;
      }
      catch (Exception e) {
        testArgs = false;
        throw dateFormat;
      }
    }
    return tempDate;
  }
  
  /**
   * returns a nice string value.
   * @return String value of object.
   */
  @Override
  public String toString() {
    return this.sourceName + "'s baseline energy consumption for " 
                           + thedate.getYear() + "-"
                           + thedate.getMonth() + "-"
                           + thedate.getDay()
                           + " was set.";
  }
  
  /**
   * Returns baselineData.
   * 
   * @return Map<String,Double> containing baseline data
   */
  public Map<String,Double> getBaselineData() {
    return this.baselineData;
  }
  
  /**
   * Returns help string.
   * 
   * @return the help string
   */
  @Override
  public String getHelpString() {
    return "set-baseline [tower|lounge] [date](optional)\n"
    + "Defines baseline power levels for specific towers/lounges "
    + "for each hour over a 24-hour period. Date parameter is optional, "
    + "if no date is provided, yesterday is used as default. ";
  }


  @Override
  public void execute(String args, WattDepotClient client) throws Exception {
    System.out.println("This shouldn't be reached.");
    
  }

}
